<?php
/**
 * Created by PhpStorm.
 * User: yf
 * Date: 2018/3/3
 * Time: 下午6:41
 */

namespace App\Utility;

use App\Common\GameCommon;
use App\Common\BaseComm;
use App\GameService\games\PlayDZPoker;
use App\GameService\games\PlayGrabNine;
use App\GameService\games\PlaySanGong;
use App\GameService\games\PlayTbNiuNiu;
use App\GameService\games\PlayBaiRen;
use App\GameService\games\PlayNiuNiu;
use App\GameService\games\PlayTwoEight;
use App\GameService\games\PlayZhaJinHua;
use EasySwoole\EasySwoole\Logger;


class BaseEnum {
    
    /**
     * TODO 通用状态值，游戏步骤 结算完成
     * @var int
     */
    const GAME_STEP_0 = 0;  //默认匹配
    const GAME_STEP_1 = 1;  //抢庄
    const GAME_STEP_2 = 2;  //下注
    const GAME_STEP_3 = 3;  //发牌
    const GAME_STEP_4 = 4;  //拼牌完成
    const GAME_STEP_5 = 5;  //结算完成


    const XYM_FE0 = 0XFE0;  //强制退出协议
    
    //TODO 机器人协议数据
    const ROBOT_XYM_FF0 = 0XFF0;
    
    
	/**
	 * TODO 协议类型定义
	 * @var 16进制值
	 */
	const XYM_0X00 = 0x00;  //心跳数据包数据
    const XYM_0X01 = 0x01;  //通用消息返回协议
	const XYM_0X02 = 0x02;  //通用错误返回协议
	const XYM_0X03 = 0x03;  //空协议（返回大厅）
	const XYM_0X04 = 0x04;  //心跳协议（返回大厅）
	const XYM_0X05 = 0x05;  //全局更用户分数的协议
	const XYM_0X06 = 0x06;  //默认后台休息游戏处理协议(切换页面，离开网页停掉数据)
	const XYM_0X07 = 0x07;  //游戏数据中赢家公告
	const XYM_0XAO = 0xA0;  //主动推送出来的赢家公告
	const XYM_0X08 = 0x08;  //掉线推送正在游戏中的数据
	const XYM_0X09 = 0x09;  //监听执行下一步操作（如监听前端抢庄动画执行完再开启下注倒计时）
	const XYM_0X10 = 0x10;  //定时器倒计器的协议
	const XYM_0X11 = 0x11;  //登录验证协议码 
	const XYM_0X12 = 0x12;  //定时展示百人牛牛房间状态数据值
	const XYM_0X13 = 0x13;	//大厅每个游戏在线用户统计
	const XYM_0X14 = 0x14;	//游戏大厅
	const XYM_0X15 = 0x15;	//进入单个游戏
	const XYM_0X16 = 0x16;	//进入游戏房间（匹配玩家）
	const XYM_0X17 = 0x17;	//游戏开局发牌(抢庄下注结束后)  如果玩家提前发三张牌再发两张牌，用参数控制 ；用同一个协议；
	const XYM_0X18 = 0x18;  //游戏抢庄（玩家抢庄）
	const XYM_0X19 = 0x19;  //游戏下注（玩家下注）
	const XYM_0X1A = 0x1a;  //游戏抢庄成功（倒计时结束）
	const XYM_0X1B = 0x1b;  //游戏下注成功后发牌操作（直接返回前端---只查看玩家自己的牌值）
	const XYM_0X1C = 0x1c;  //结算成功后显示牌值（直接返回前端---所有玩家牌值）
	const XYM_0X1D = 0x1d;  //停止匹配（匹配取消）
	const XYM_0X1E = 0x1e;  //游戏汇总数据推出
	const XYM_0X1F = 0x1f;  //掉线重连数据；
	const XYM_0X20 = 0x20;  //退出游戏操作（退出当前游戏房间）
	const XYM_0X21 = 0x21;  //玩家拼牌操作；
	const XYM_0X22 = 0x22;  //玩家退出游戏	
	const XYM_0X24 = 0x24;  //玩家游戏记录
	const XYM_0X25 = 0x25;  //玩家游戏牌数据记录
	const XYM_0X26 = 0x26;  //玩家游戏牌数据记录
	const XYM_0X30 = 0x30;  //二八杠游戏中摇色子
	const XYM_0X50 = 0x50;  //进入房间（百人牛牛）
	const XYM_0X51 = 0x51;  //历史结果显示（百人牛牛）
	const XYM_0X52 = 0x52;  //下注位置数据展示（百人牛牛））
	const XYM_0X53 = 0x53;  //玩家未下注消息提交（百人牛牛））
	const XYM_0X55 = 0x55;  //游戏继续连续下一局（百人牛牛））
	const XYM_0X56 = 0x56;  //游戏玩家前20名（百人牛牛））
	
    /* const BR_XYM_0X51 = 0x51;  //百人牛牛准备
    const BR_XYM_0X52 = 0x52;  //百人牛牛洗牌
    const BR_XYM_0X54 = 0x54;  //百人牛牛下注
    const BR_XYM_0X55 = 0x55;  //百人牛牛停止下注
    const BR_XYM_0X56 = 0x56;  //百人牛牛派奖
    const BR_XYM_0X57 = 0x57;  //百人牛牛休息 */
	
	const XYM_0X60 = 0x60;  //显示携带币
	const XYM_0X61 = 0x61;  //显示携带币
	const XYM_0X62 = 0x62;  //发公共牌操作
	const XYM_0X63 = 0x63;  //玩家下注操作
	const XYM_0X64 = 0x64;  //发牌时计算牌型推送结果
	
	const XYM_0X71 = 0x71;  //玩家看牌操作
    const XYM_0X73 = 0x73;  //玩家下注操作
    const XYM_0X74 = 0x74;  //玩家比牌操作
    
	/**
	 * TODO 错误信息码; 
	 * 0XF：为正常/成功
	 * @var 16进制值
	 */
	const MSG_0XF = 0X0;
	/**
	 *  TODO 0XF0 列表为空
	 * @var 16INT
	 */
	const MSG_0XF0 = 0XF0;
	const MSG_0XF1 = 0XF1;
	const MSG_0XF2 = 0XF2;
	const MSG_0XF3 = 0XF3;
	const MSG_0XF4 = 0XF4;
	const MSG_0XF5 = 0XF5;
	const MSG_0XF6 = 0XF6;
	/**
	 * 0XF7 玩家未登录
	 * @var 16INT
	 */
	const MSG_0XF7 = 0XF7;
	const MSG_0XF8 = 0XF8;
	const MSG_0XF9 = 0XF9;
	/**
	 * TODO 系统异常
	 * @var 16INT
	 */
	const MSG_0XFA = 0XFA;
	const MSG_0XFB = 0XFB;
	const MSG_0XFC = 0XFC;
	const MSG_0XFD = 0XFD;
	const MSG_0XFF = 0XFF;
	const MSG_0XE0 = 0XE0;
	const MSG_0XE1 = 0XE1;
	const MSG_0XE2 = 0XE2;
	const MSG_0XE3 = 0XE3;
	const MSG_0XE4 = 0XE4;
	const MSG_0XE5 = 0XE5;
	
    /**
     * TODO 定义接口路由；
     */
    const REQUEST_GROUTES = [
        0x03 => 'games-doCenter',       //空协议
        0x04 => 'index-doPing',         //PING数据包
        0x06 => 'games-doGameOut',      //切换页面，离开网页停掉数据
        0x07 => 'games-doNotices',      //通用的游戏数据获取
        0x09 => 'games-doNextStep',     //通用下一步
        0x11 => 'guser-login',          //登录
        //0x12 => 'guser-loginout',     //登出
        //0x13 => 'guser-getUser',      //获取登录用户
        0x14 => 'index-gamelist',     //游戏大厅
        0x15 => 'index-gameDetail',   //进入单个游戏
        0x16 => 'games-play',         //进入游戏房间
        0x17 => 'games-opening',      //游戏开局发牌
        0x18 => 'games-playGameRush', //游戏抢庄
		0x19 => 'games-playGameBets', //游戏下注
        0x1d => 'games-playCancel',   //停止匹配（匹配取消）
        0x1f => 'index-gameReConnect',//牛牛掉线重连（获取数据）
        0x21 => 'games-playSplit',    //玩家拼牌
        0x22 => 'games-gameOver',     //玩家退出游戏房间
        0x23 => 'games-gameRules',    //玩家游戏规则
        0x24 => 'games-playOrder',    //玩家订单记录
        0x26 => 'games-playFlip',     //玩家翻牌
        0x50 => 'Gamehund-play',      //百人牛牛进入游戏
        0x56 => 'Gamehund-fronts',    //百人牛牛玩家前20名的数据
        0x60 => 'gamepork-showRoomChip',      //携带筹码显示（进入房间）
        0x61 => 'gamepork-goRoomChip',        //保存携带筹码（保存操作）
        0x63 => 'gamepork-goBets',            //玩家下注操作（弃牌，跟注，加倍）
        0x70 => 'gamejinhua-play',    //炸金花接入游戏
        0x71 => 'gamejinhua-playFlip',      //炸金花看牌
        0x73 => 'gamejinhua-playBets',      //玩家下注操作（弃牌，跟注，加倍）
        0x74 => 'gamejinhua-playCompare',  //炸金花比牌

        0XFF0 => 'gamerobot-login',   //机器人连接登录（验证是否为机器人的操作）
        0XFF1 => 'gamerobot-doplay',  //机器人进入游戏(主动进入游戏)
        0XFF3 => 'gamerobot-doBets',   //机器人自动下注
        0XFF4 => 'gamerobot-loginOut' //机器人登出
    ];
	
    //TODO 强行退出的消息协议码
    const RESPOSE_MSG_OUTS = [
        self::MSG_0XF1,
        self::MSG_0XF3,
        self::MSG_0XF4,
        self::MSG_0XF7,
    ];
    
    /**
     * TODO 定义返回消息记录编码;
     */
    const RESPONSE_MESSAGES = [
        0X0 => '操作成功',
        0XF0 => '列表为空',
        0XF1 => '登录失败',
        0XF2 => '非法请求',
        0XF3 => '账号不存在',
        0XF4 => '当前账号异常',
        0XF5 => '游戏类型异常，或已下架',
        0XF6 => '游戏房间异常',
        0XF7 => '玩家未登录或已掉线，请重试',
		0XF8 => '玩家游戏分不达标!',
		0XF9 => '玩家匹配不成功，退出房间',
		0XFA => '系统异常',		
		0XFB => '玩家抢庄失败',
		0XFC => '玩家下注失败',
        0XFD => '非下注状态',
        0XFF => '拼牌信息错误',
        0XE0 => '请等待上一局游戏结束',
        0XE1 => '超出当前位置限红额度',
        0XE2 => '已经连续3局未下注，连续5局未下注将被请离房间',
        0XE3 => '已经连续5局未下注，被请离了游戏桌',
        0XE4 => '非翻牌状态',
        0XE5 => '玩家下线，游戏结束'
    ];
    
    //TODO 游戏状态===fjStatus
    const GAME_STEP = [
        0 => '匹配',
        1 => '抢庄',
        2 => '下注',
        3 => '发牌',
        4 => '拼牌',
        5 => '结算',
    ];
    
    
    /**
     * TODO 初始化数据
     */
    function __construct(){
    }
    
    /**
     * TODO 登录不验证的协议
     * @return string[]
     */
    public static function getNoLoginXYM() {
        return [
            self::XYM_0X00,
            self::XYM_0X01,
            self::XYM_0X02,
            self::XYM_0X10,
            self::XYM_0X11,
            self::XYM_0X22,
            self::ROBOT_XYM_FF0,
        ];
    }
    
    /**
     *  TODO获取当前时间格式；默认为STRING
     * @param boolean $date 为false 显示时间戳, 为true显示时间格式 ；
     * @param boolean $timestamp $date=true生效； 时间戳
     */
    public static function getNowTime($date = FALSE , $timestamp = false){
        $nowtime = time();
        if($date==true){    //用String
            $nowtime = ($timestamp > 0) ? $timestamp : $nowtime ;
            $nowtime = date('Y-m-d H:i:s' , $nowtime); //取时间 
        }
        return $nowtime;
    }
    
    //TODO 获取时间间隔多长
    public static function getNowTimeLong ($firstStamp = 0){
        $nowtime = time();
        $long = ($firstStamp>0 && $firstStamp < $nowtime) ? $nowtime - $firstStamp : 0;
        return $long;
    }
    
    
    
    /**
     * TODO 登录本地IP；
     */
    public static function getClientIP(){
        //self::Logger("SER=".json_encode($_SERVER),"user-login");
        return BaseComm::getRealClientIP();
    }
    
    /**
     *  TODO 组合两个数组，把特定值提前
     * @param array $cards
     * @param string $groupstr
     * @return array|array
     */
    public static function changeGroupArray($cards,$groupstr = '') {
        $xianjCard = $cards;
        $xjfrontArr = (isset($groupstr) && !empty($groupstr)) ? explode(',', $groupstr) : FALSE;
        if(isset($xjfrontArr) && !empty($xjfrontArr) && sizeof($xjfrontArr) == 3){
            foreach ($xjfrontArr as &$rv){$rv = intval($rv); }
            $xianjCard = array_merge($xjfrontArr,array_diff($cards,$xjfrontArr));  //排序操作
        }
        return $xianjCard;
    }
    
    /**
     * TODO Array转JSON
     * @param array $res
     * @return string| JSON
     */
    public static function JsonEncode($res,$jsonFlag = JSON_FORCE_OBJECT){
        if($res !== FALSE && is_array($res)){ 
            return json_encode($res,$jsonFlag); //只转化为JSON字符串
        } return $res;
    }
    
    /**
     * TODO JSON解码
     * @param string $res
     * @return string| DESC
     */
    public static function JsonDcode($res,$toArray = true){
        $arr = FALSE;
        if($res !== FALSE && is_string($res)){
            $arr = json_decode($res,$toArray);          //JSON字符串转Array
        } 
        return $arr;
    }

    /**
     *TODO 生成Token
     *
     * @param $uid
     * @param $ip
     * @return string
     */
    public static function createToken($uid,$ip,$currentTime){
        $singleToken = '';
        if (empty($uid) || empty($ip)) return $singleToken;
        $tokenStr = $uid.$ip.$currentTime;
        $singleToken = md5($tokenStr);
        return $singleToken;
    }
    
    /**
     * TODO 加密字段串
     */
    public static function descEncode($key, $str,$blocksize = 16){
        $text = trim($str);
        $pad = $blocksize - (strlen($text) % $blocksize);
        $laststr = $text . str_repeat(chr($pad), $pad);
        $encrypt_str = openssl_encrypt($laststr, 'AES-128-ECB', $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING); //加密串
        return base64_encode($encrypt_str);
    }
    
    
    /**
     * TODO  解密字段串
     */
    public static function descDecode($key, $str) {
        $str = base64_decode($str);
        $decrypt_str = openssl_decrypt($str, 'AES-128-ECB', $key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING); //解密串
        $pad = ord($decrypt_str{strlen($decrypt_str)-1});   //ASCII 值
        if ($pad > strlen($decrypt_str)) return false;
        if (strspn($decrypt_str, chr($pad), strlen($decrypt_str) - $pad) != $pad) return false;
        $last_str = substr($decrypt_str, 0, -1 * $pad);
        return trim($last_str);
    }

    /**
     * TODO MD5加密KEY
     * @param string $key MD5加密KEY
     * @param string $char 加密字段
     * @return string 加密后的签名
     */
    public static function MD5($key,$char = "") {
        return strtoupper(md5($char.$key));
    }
    
    
    /**
     * TODO 本地调试的代码输出内容；
     * TODO 统一操作数据； 
     */
    public static function Logger($message="",$objectcls = false){
        //TODO 正常写入日志内容
        Logger::getInstance()->log($message, Logger::LOG_LEVEL_INFO);
        //Logger::getInstance()->console($message,Logger::LOG_LEVEL_INFO);
    }
    
    
    //TODO 存储数据
    public static function wirteRedisData($rediskey,$savecontent,$hkey=false,$timelong = 2000){
        
        try {
            if (isset($hkey) && !empty($hkey)) { //Hash保存数据；
                if($savecontent === -1){
                    //$redis->hDel($rediskey, $hkey); //删除
                    RedisTools::Pool_hDel($rediskey, $hkey); //删除
                }else{
                    //$redis->hSet($rediskey, $hkey, $savecontent);
                    RedisTools::Pool_hSet($rediskey, $hkey, $savecontent);
                }
            }else{
                if($savecontent === -1){
                    //$redis->del($rediskey);
                    RedisTools::Pool_del($rediskey);
                }else{
                    //$redis->setRedisValue($rediskey, $savecontent, $timelong);
                    RedisTools::Pool_set($rediskey, $savecontent, $timelong);
                }
            }
        } catch (\Throwable $throwable) {
        }
        return true ;
    }
    
    
    /**
     * TODO 获取执行类
     * @param string $gameName
     * @param boolean $iftrue
     */
    public static function getClassObject($gameName,$gameId = false) {
        $oneObj = null;
        //设计成单列模式；实例化时直接调用 ；  直接用Class：getInstance();  LineRooms:RoomMaps:Rooms 操作
        if(!$gameName || intval($gameId) > 0){  //取游戏名
            $gameName = GameCommon::getGameName($gameId,'gameKey');
        }
        if(!$gameName) return false;
        $gameName = strtoupper($gameName);                          //默认为名称
        switch ($gameName){
            case 'QZNN':
            case 'LOOKQZNN': $oneObj = new PlayNiuNiu();break;      //抢庄牛牛[含看牌]
            case 'TBNN':    $oneObj = new PlayTbNiuNiu();break;     //通比牛牛
            case 'TWOEBAR': $oneObj = new PlayTwoEight();  break;   //二八杠
            case 'QZGRAB': $oneObj = new PlayGrabNine();  break;    //抢庄牌九
            case 'SANGG': $oneObj = new PlaySanGong();  break;      //三公
            case 'BRNN': $oneObj = new PlayBaiRen(); break;         //百人牛牛
            case 'DZPK': $oneObj = new PlayDZPoker();  break;       //德洲扑克
            case 'ZJH' : $oneObj = new PlayZhaJinHua();break;       //炸金花
            default: break;
        }
        if(!empty($oneObj) && is_object($oneObj)){
            $oneObj->setGameName($gameName);
        }
        return $oneObj;
    }
    
    
    /**
     * TODO 计算ping一个IP地址，能不能通
     * @param boolean $ipaddr
     * @return boolean|number
     */
    public static function pingCount ($ipaddr = FALSE){
        if(!$ipaddr) return  FALSE;
        //$ipaddr = '127.0.0.1';
        $start_mic = BaseComm::getMillisecond();
        $myArr = explode(':', $ipaddr);
        $connHost = !empty($myArr) ? reset($myArr): FALSE;
        $connPort = (!empty($myArr) && isset($myArr[1]) && !empty($myArr[1]) && intval($myArr[1]) > 0) ? intval($myArr[1]): 80;
        //var_dump($connHost .':port '.$connPort.' =='. json_encode($myArr));
        
        //创建头部数据
        $socketHead = FALSE;    //Head头部
        if(filter_var($connHost, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ){           //IPv6
            $socketHead = @socket_create(AF_INET6, SOCK_STREAM, SOL_TCP);
        }elseif( filter_var( $connHost, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ){    //IPv4
            $socketHead = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        }else {
            $socketHead = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        }
        if(!$socketHead) return FALSE;
        //创建Socket连接Client;
        $connect_ok = @socket_connect($socketHead, $connHost, $connPort);
        //连接对象值
        if(!$connect_ok){
            //$errorid = socket_last_error($socketHead);
            //$error = iconv('GBK', "UTF-8", socket_strerror( $errorid ));
            return FALSE;
        }
        @socket_close($socketHead);             //结束时长
        $finish_mic = BaseComm::getMillisecond(); 
        $endFinish = $finish_mic - $start_mic ; //连接时长计算
        //var_dump($finish_mic.' -- ' . $start_mic . ' ===long:' . $endFinish);
        return $endFinish;
    }
    
    
}